package fileDemo;

import org.junit.Test;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author Ajie
 * @date 6/24/22
 * @function 随机读写 + buffer. 减少OOM
 */
public class RandomAccessFileDemo {
    static String srcFile = "./src/fileDemo/mfile/test/text";;
    static String destFile = "./src/fileDemo/mfile/test/textcopy";;

    public static void main(String[] args) {

        //int i = 1024 * 1024 * 1024;//<0 ＞ int 最大值 2,147,483,647
        //System.out.println("len:"+i);

        String srcFilePath = "/Users/macpro/Downloads/BXMJ_2G_DVT_2.6.2.63.d.s.d_07181514-ota.zip";
//        String srcFilePath = "/Users/macpro/Movies/当幸福来敲门.mkv";
        String destFilePath = "/Users/macpro/Downloads/";

        try {
            RandomAccessFile rafi = new RandomAccessFile(srcFilePath, "r");
            //FileOutputStream fileOutputStream = new FileOutputStream(destFilePath);
            ByteBuffer byteBuffer = rafi.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, rafi.length());//Size exceeds Integer.MAX_VALUE
            System.out.println("RandomAccessFileDemo:main:"+byteBuffer.position());


            unmap(byteBuffer);

            //2022-06-25 12:02:42 对比测试，没有数量级上的差异。 不知道 memeory 上是否有很大差异。待排查。
            //1.406s
            randomCopyFileFixBuffer(srcFilePath,destFilePath+"buffer.zip",100,1024*1024*1024);

            //0.842s
            //customRandomCopyFileChannel(srcFilePath,destFilePath+"channel.zip",100,1024*1024*1024);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    //反射释放 buffer
    public static void unmap(ByteBuffer byteBuffer) {
        Class<?> aClass = null;
        try {
            if (byteBuffer == null) {
//                Log.i("IOUtil", "byteBuffer is null not unmap");
                return;
            }
            aClass = Class.forName("sun.nio.ch.FileChannelImpl");
            java.lang.reflect.Method unmap = aClass.getDeclaredMethod("unmap", MappedByteBuffer.class);
            unmap.setAccessible(true);
            unmap.invoke(null,byteBuffer);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //success
    @Test
    public void test() throws IOException {

        File file = new File(destFile);
        if (file.exists())file.delete();
        /*
         * 测试结果与Buffer size有关
         */
        // 1、使用MappedByteBuffer: 0.7s
        RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
        RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");
        FileChannel fci = rafi.getChannel();
        FileChannel fco = rafo.getChannel();
        long size = fci.size();
        MappedByteBuffer mbbi = fci.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, 0, size);
        long start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            byte b = mbbi.get(i);
            mbbo.put(i, b);
        }
        fci.close();
        fco.close();
        rafi.close();
        rafo.close();
        System.out.println("Spend: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");

    }

//    @Test //success  随机读写 + buffer. 成功。 macbookpro 1G 0.9s
    //优先使用，缓存内存大小可控。
    public static void randomCopyFileFixBuffer(String srcFilePath, String destFilePath, int offset, int len) throws IOException {
        if (len <0 ){
            System.err.println("len < 0 !!!");
            return;
        }
        File srcFile = new File(srcFilePath);
        if (!srcFile.exists()){
            System.err.println("this file is not exit:"+srcFilePath);
            return;
        }
        // 2、自己处理Buffer(RandomAccessFile): 0.13s
//        String srcFilePath = "/Users/macpro/Downloads/00111-BXMJ_2G_DVT_2.6.2.63.d.s.d_06240942-ota.zip";
//        String destFilePath = "/Users/macpro/Downloads/copy-BXMJ_2G_DVT_2.6.2.63.d.s.d_06240942-ota.zip";
//        int len = 1024 * 1024 * 1024;//总大小。需要读取的大小
//        int offset = 2;//开始读取的偏移量
        File file = new File(destFilePath);
        if (file.exists())file.delete();
        RandomAccessFile rafi = new RandomAccessFile(srcFilePath, "r");
        RandomAccessFile rafo = new RandomAccessFile(destFilePath, "rw");


        int bufferSize = 1024 * 1024;//
        byte[] buf = new byte[bufferSize];



        long start = System.currentTimeMillis();
        try {
            rafi.seek(0);
            rafi.skipBytes(offset);

            int c = 0;
            //第一次读取，
            if (len <= bufferSize){//读取的总量 < filesize,所以做截取
                c = rafi.read(buf,0,len);
            }else {
                c = rafi.read(buf);
            }

            int count = c;
            while (c > 0) {
                //写入
                if (c == buf.length) {
                    rafo.write(buf);
                } else {
                    rafo.write(buf, 0, c);
                }

                if (count >= len){ //读取，写入完成。
                    break;
                }

                //读取
                if (count + bufferSize >=len){//读取的总量 < filesize,所以做截取
                    int currentlen =  len - count;
                    c = rafi.read(buf,0,currentlen);
                }else {
                    c  = rafi.read(buf);
                }
                count = count+c;
//                System.out.println("read,len:"+c +" read,count:"+count);//字符串拼接 很耗时。
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rafi.close();
            rafo.close();
        }

        System.out.println("customRandomCopyFileFixBuffer Spend: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
    }

    public static void copyWindow(String[] args) {
        //head

        //last
    }

    //优先使用，缓存内存大小可控。
    public static ByteBuffer copyBufferFromFile(RandomAccessFile rafi) throws IOException {
        if (rafi == null) return null;
//        if (len <0 ){
//            System.err.println("len < 0 !!!");
//            return;
//        }

        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4096 + 1000);
//        int len = byteBuffer.limit();
//        long length = rafi.length();
//        len = len > rafi.length() ? (int) rafi.length() : len;
        rafi.getChannel().read(byteBuffer);
        return byteBuffer;
    }


//    @Test //success  随机读写 + buffer. 成功。 macbookpro 1G 0.9s
    //Size exceeds Integer.MAX_VALUE  当文件 > 2147483647   2.1G的时候
    public static void customRandomCopyFileChannel(String srcFilePath,String destFilePath,int offset,int len) throws IOException {
        if (len < 0 ){
            System.err.println("len < 0 !!!");
            return;
        }
        File srcFile = new File(srcFilePath);
        if (!srcFile.exists()){
            System.err.println("this file is not exit:"+srcFilePath);
            return;
        }

        // 2、自己处理Buffer(RandomAccessFile): 0.13s
//        String srcFilePath = "/Users/macpro/Downloads/00111-BXMJ_2G_DVT_2.6.2.63.d.s.d_06240942-ota.zip";
//        String destFilePath = "/Users/macpro/Downloads/copy-BXMJ_2G_DVT_2.6.2.63.d.s.d_06240942-ota.zip";
//        int len = 1024 * 1024 * 1024;//总大小。需要读取的大小
//        int offset = 2;//开始读取的偏移量
        File file = new File(destFilePath);
        if (file.exists())file.delete();
        RandomAccessFile rafi = new RandomAccessFile(srcFilePath, "r");
        FileOutputStream fileOutputStream = new FileOutputStream(destFilePath);
        MappedByteBuffer byteBuffer = rafi.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, rafi.length());//Size exceeds Integer.MAX_VALUE


        long start = System.currentTimeMillis();
        try {
            rafi.seek(0);
            byteBuffer.duplicate();
            byteBuffer.position(offset).limit(offset+len);
            fileOutputStream.getChannel().write(byteBuffer);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rafi.close();
            fileOutputStream.close();
        }

        System.err.println("customRandomCopyFileChannel Spend: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
    }



    @Test //success
    public void test2() throws IOException {
        // 2、自己处理Buffer(RandomAccessFile): 0.13s
        RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
        RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");

        byte[] buf = new byte[1024 * 8];

        long start = System.currentTimeMillis();

        int c = rafi.read(buf);

        while (c > 0) {
            if (c == buf.length) {
                rafo.write(buf);
            } else {
                rafo.write(buf, 0, c);
            }

            c = rafi.read(buf);
        }
        rafi.close();
        rafo.close();
        System.out.println("Spend: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");

    }



    //copy failed
    @Test
    public void test3() throws IOException {

        File file = new File(destFile);
        if (file.exists())file.delete();

        // 3、BufferedInputStream&BufferedOutputStream: 3.02s
        FileInputStream rafi = new FileInputStream(srcFile);
        FileOutputStream rafo = new FileOutputStream(destFile);

        BufferedInputStream bis = new BufferedInputStream(rafi, 8192);
        BufferedOutputStream bos = new BufferedOutputStream(rafo, 8192);
        long size = rafi.available();

        long start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            byte b = (byte) bis.read();
            bos.write(b);
        }
        rafi.close();
        rafo.close();
        System.out.println("Spend: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
    }
}

